/**
 * 
 */
package com.google.code.cisx.wfe.model;

import java.util.ArrayList;
import java.util.List;

import com.google.code.cisx.wfe.events.EventManager;
import com.google.code.cisx.wfe.events.PlaceEvent;
import com.google.code.cisx.wfe.events.PlaceEvent.PlaceEventType;

/**
 * Place is an entity that contributes towards the state of a Case in
 * conjunction with a {@link Token}. {@link Place} models a Condition.
 * 
 * @author <a href='mailto:rahul.thakur.xdev@gmail.com'>Rahul Thakur</a>
 * @since 1.0
 * @version $Id$
 */
public class Place<D> extends AbstractPetriNetEntity<D> {

    /**
     * Tokens put as a result of firing {@link Transition}(s) that connect to
     * this {@link Place}.
     */
    private List<Token<?>> tokens;

    /**
     * Number of tokens that this {@link Place} can hold.
     * <p>
     * A value of <i>Zero</i> implies no limit.
     */
    private int threshold;

    /**
     * {@link InputArc}s that connect to this Place.
     */
    private List<InputArc<?>> inputs;

    /**
     * {@link OutputArc}s from this Place.
     */
    private List<OutputArc<?>> outputs;

    /**
     * Creates a new Place with a unlimited threshold for {@link Token}s
     * allowed.
     * 
     * @param label
     */
    public Place(String label) {
        this(label, 0); // threshold '0' implies unlimited Tokens.
    }

    /**
     * 
     * @param label
     * @param threshold maximum number of Tokens this Place can hold.
     */
    public Place(String label, int threshold) {
        super(label);
        this.threshold = threshold;
    }

    /**
     * Returns list of all {@link Token}s put on this Place.
     * 
     * @return
     */
    public List<Token<?>> getTokens() {
        return this.tokens;
    }

    /**
     * Adds the specified {@link Token} to this Place.
     * 
     * @param t
     */
    public void addToken(Token<?> t) {
        if (threshold > 0 && this.tokens.size() == threshold) {
            // TODO: Throw a Threshold exceeded exception
            // A place can only have a certain number of tokens.
        }

        if (null == this.tokens)
            this.tokens = new ArrayList<Token<?>>();
        this.tokens.add(t);

        EventManager.getInstance().dispatchEvent(
                new PlaceEvent(this, PlaceEventType.TOKEN_ADDED));
    }

    /**
     * Removes the specified {@link Token} off this Place.
     * 
     * @param t
     */
    public void removeToken(Token<?> t) {
        if (null != this.tokens && this.tokens.contains(t)) {
            this.tokens.remove(t);
            EventManager.getInstance().dispatchEvent(
                    new PlaceEvent(this, PlaceEventType.TOKEN_DELETED));
        }
        // else ignore silently
    }

    /**
     * Max number of {@link Token}s that this Place instance can hold.
     * 
     * @return
     */
    public int getThreshold() {
        return this.threshold;
    }

    /**
     * 
     * @param p
     */
    public void addInput(InputArc<?> i) {
        if (null == inputs)
            this.inputs = new ArrayList<InputArc<?>>();
        this.inputs.add(i);
    }

    /**
     * 
     * @param p
     */
    public void addOutput(OutputArc<?> o) {
        if (null == outputs)
            this.outputs = new ArrayList<OutputArc<?>>();
        this.outputs.add(o);
    }

    /**
     * Removes the specified {@link InputArc} connection to this Transition.
     * 
     * @param i
     */
    public void removeInput(InputArc<?> i) {
        if (null != inputs && this.inputs.contains(i))
            this.inputs.remove(i);
    }

    /**
     * Removes the specified {@link OutputArc} connection from this Transition.
     * 
     * @param o
     */
    public void removeInput(OutputArc<?> o) {
        if (null != outputs && this.outputs.contains(o))
            this.outputs.remove(o);
    }

    /**
     * @return the inputs
     */
    public List<InputArc<?>> getInputs() {
        return inputs;
    }

    /**
     * @return the outputs
     */
    public List<OutputArc<?>> getOutputs() {
        return outputs;
    }
}
